﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using PS3Helpers.Helpers;

namespace PS3Helpers
{
    public class PS3Builder
    {
        private readonly PS3PackageFile _packageFile;
        private readonly string _workingDirectory;
        private readonly OutputLogInterface _logInterface;
        private readonly string _signedElfFilename;
        private readonly string _makeFilename;

        public PS3Builder(OutputLogInterface loginterface, PS3PackageFile packagefile, bool rebuild, string makefilename)
        {
            _logInterface = loginterface;
            _packageFile = packagefile;
            _makeFilename = makefilename;

            if (string.IsNullOrEmpty(_packageFile.Filename))
                throw new Exception("Can't build before package file has been saved to the folder containing the MakeFile file");

            if (!string.IsNullOrEmpty(makefilename))
            {
                _workingDirectory = Path.GetDirectoryName(makefilename);
            }
            else
            {
                _workingDirectory = Path.GetDirectoryName(_packageFile.Filename).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
            }

            _workingDirectory = _workingDirectory.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;

            
            Build(rebuild);
            _signedElfFilename = GenerateSignedElf();
        }

        public string SignedElfFilename
        {
            get { return _signedElfFilename; }
        }

        #region Build code

        private void Build(bool rebuild)
        {
            _logInterface.SetCurrentStep("Compiling");

            string buildparameter = rebuild ? "-B " : "";
            if (!string.IsNullOrEmpty(_makeFilename))
            {
                string mkf = _makeFilename;
                if (mkf.StartsWith(_workingDirectory, StringComparison.CurrentCultureIgnoreCase))
                {
                    mkf = mkf.Substring(_workingDirectory.Length);
                }

                buildparameter += "--makefile=" + mkf;
            }

            using (ProcessExecutor makeprocess = new ProcessExecutor(_logInterface, _workingDirectory, "make", buildparameter, 5, false))
            {
                if (makeprocess.ExitCode != 0)
                    throw new Exception("Build failed! Make returned: " + makeprocess.ExitCode.ToString());
            }
        }

        #endregion Build code

        #region Convert elf to self / bin

        private string GenerateSignedElf()
        {
            // Generate signed ELF: (run make_fself_npdrm.exe filename.elf filename.self)
            _logInterface.SetCurrentStep("Generating signed elf");

            string elffilename;
            if (!string.IsNullOrEmpty(_makeFilename))
            {
                elffilename = GetTargetFilename(_makeFilename);
            }
            else
            {
                elffilename = GetTargetFilename(_workingDirectory + "Makefile");
            }

            
            string resultfilename = Path.ChangeExtension(elffilename, "self");

            if (File.Exists(resultfilename))
                File.Delete(resultfilename);


            StringBuilder parameters = new StringBuilder();
            parameters.Append("\"");
            parameters.Append(Path.GetFileName(elffilename));
            parameters.Append("\"");

            parameters.Append(" ");

            parameters.Append("\"");
            parameters.Append(Path.GetFileName(resultfilename));
            parameters.Append("\"");

            using (ProcessExecutor convertprocess = new ProcessExecutor(_logInterface, Path.GetDirectoryName(elffilename), "make_fself_npdrm.exe", parameters.ToString(), 5, true))
            {
                if (convertprocess.ExitCode != 0)
                    throw new Exception("Failed to sign elf file! make_fself_npdrm.exe returned: " + convertprocess.ExitCode.ToString());
            }

            return resultfilename;
        }

        #endregion Convert elf to self / bin

        private string GetTargetFilename(string makefilename)
        {
            string[] rows = File.ReadAllLines(makefilename);

            foreach (string row in rows)
            {
                string trimmedrow = row.Trim();

                if (trimmedrow.StartsWith("PPU_TARGET", StringComparison.CurrentCultureIgnoreCase))
                {
                    trimmedrow = trimmedrow.Substring(10); // Remove "PPU_TARGET"
                    trimmedrow = trimmedrow.Trim();

                    if (!trimmedrow.StartsWith("="))
                        throw new Exception("Malformed PPU_TARGET in makefile");

                    trimmedrow = trimmedrow.Substring(1); // Remove "="
                    trimmedrow = trimmedrow.Trim();

                    string makepath = Path.GetDirectoryName(makefilename);
                    if (!makepath.EndsWith(Path.DirectorySeparatorChar.ToString()))
                        makepath = makepath += Path.DirectorySeparatorChar;

                    return Path.Combine(makepath, trimmedrow);
                }
            }

            throw new Exception("Failed to find PPU_TARGET in makefile!");
        }
    }
}
